/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = rtp_header.c                                       */
/*  NOTE      =                                                    */
/*  DATE      = 2015/08/20 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <frct_comm.h>
#include <ctype.h>

#include "rtp_comm.h"
#include "rtp_core.h"

/*******************************************************************************
*  FUNC     :  分析RTP头
*  ARGS     :  *p_st_detect_sess                    (detect过程中的中间数据)
*           :  *p_payload                           (数据内容)
*           :  ui_payload_len                       (数据长度)
*           :  ui_dst_ip                            (数据包的目的IP)
*           :  us_dst_port                          (数据包的目的端口)
*  RTN      :  APM_L7_IS_YES                        (是RTP协议)
*              APM_L7_IS_UNKNOWN                    (不能判断该协议)
*  NOTE     :
*******************************************************************************/
int rtp_header_detect(Rtp_detect_session *p_st_detect_sess
                    , char *p_payload
                    , unsigned int ui_payload_len
                    , unsigned int ui_dst_ip
                    , unsigned short us_dst_port)
{
    unsigned char uc_tmp;
    int i_version;
    int i_padding;
    int i_extension;
    int i_cc_count;
    int i_marker;
    int i_payload_type;
    int i_loop;
    unsigned int ui_pkt_offset = 0;
    unsigned short *p_us_seq;
    unsigned short us_seq;
    unsigned int *p_ui_timestamp;
    unsigned int ui_timestamp;
    unsigned int *p_ui_ssrc;
    unsigned int ui_ssrc;
    unsigned int ui_hdr_ext_len = 0;
    unsigned int ui_frame_offset = 0;
    unsigned short *p_us_rtp_len;
    unsigned short us_rtp_len = 0;
    char c_tcprtp_magic;
    char c_tcprtp_channel;
    Rtp_stream_type stream_type = APM_RTP_STREAM_TYPE_UNKNOWN;

redo:
    RTP_PRINT_WARN("detect_succ_count:[%u]\n", p_st_detect_sess->c_detect_succ_count);
    RTP_PRINT_WARN("pre stream type:[%s]\n", APM_RTP_STREAMTYPE2STR(p_st_detect_sess->stream_type));
    RTP_PRINT_WARN("pre payload type:[%u]\n", p_st_detect_sess->us_pre_payload_type);
    RTP_PRINT_WARN("pre seq:[%u]\n", p_st_detect_sess->us_pre_seq);
    RTP_PRINT_WARN("pre timestamp:[%u]\n", p_st_detect_sess->ui_pre_timestamp);
    RTP_PRINT_WARN("pre ssrc:[%u]\n", p_st_detect_sess->ui_pre_ssrc);
    RTP_PRINT_WARN("pre dst ip:[%u]\n", p_st_detect_sess->ui_dst_ip);
    RTP_PRINT_WARN("pre dst port:[%u]\n", p_st_detect_sess->us_dst_port);
    RTP_PRINT_WARN("pre is marker:[%s]\n", p_st_detect_sess->c_pre_is_marker != 0 ? "YES" : "NO");
    RTP_PRINT_WARN("pre skip len:[%u]\n", p_st_detect_sess->us_skip_len);

    if (p_st_detect_sess->us_skip_len > 0)
    {
        if (p_st_detect_sess->us_skip_len == ui_payload_len)
        {
            p_st_detect_sess->us_skip_len = 0;
            return APM_L7_IS_UNKNOWN;
        }
        else if (p_st_detect_sess->us_skip_len > ui_payload_len)
        {
            p_st_detect_sess->us_skip_len = (unsigned short) (p_st_detect_sess->us_skip_len - ui_payload_len);
            return APM_L7_IS_UNKNOWN;
        }
        else
        {
            p_payload = p_payload + p_st_detect_sess->us_skip_len;
            ui_payload_len = ui_payload_len - p_st_detect_sess->us_skip_len;
        }
    }

    if (p_st_detect_sess->stream_type != APM_RTP_STREAM_TYPE_UNKNOWN)
    {
        stream_type = p_st_detect_sess->stream_type;
        p_st_detect_sess->stream_type = APM_RTP_STREAM_TYPE_UNKNOWN;

        if (stream_type == APM_RTP_STREAM_TYPE_TCPRTP)
        {
            ui_hdr_ext_len = APM_TCPRTP_HDR_PKTLEN;
            ui_pkt_offset += ui_hdr_ext_len;
            c_tcprtp_magic = *(p_payload + ui_frame_offset);
            if (c_tcprtp_magic != APM_TCPRTP_HDR_MAGIC)
                goto unknown;
            c_tcprtp_channel = *(p_payload + ui_frame_offset + 1);
            if (c_tcprtp_channel != 0x00)
                goto unknown;
            p_us_rtp_len = (unsigned short *) (p_payload + ui_frame_offset + 2);
            us_rtp_len = ntohs(*p_us_rtp_len);
        }
        else if (stream_type == APM_RTP_STREAM_TYPE_TCPRTP_NONSTANDARD)
        {
            ui_hdr_ext_len = APM_TCPRTP_NONSTANDARD_HDR_PKTLEN;
            ui_pkt_offset += ui_hdr_ext_len;
            p_us_rtp_len = (unsigned short *) (p_payload + ui_frame_offset);
            us_rtp_len = ntohs(*p_us_rtp_len);
        }
    }
    else if (stream_type == APM_RTP_STREAM_TYPE_UNKNOWN)
    {
        stream_type = APM_RTP_STREAM_TYPE_RTP;
    }
    else if (stream_type == APM_RTP_STREAM_TYPE_RTP)
    {
        stream_type = APM_RTP_STREAM_TYPE_TCPRTP;
        ui_hdr_ext_len = APM_TCPRTP_HDR_PKTLEN;
        ui_pkt_offset += ui_hdr_ext_len;
        c_tcprtp_magic = *(p_payload + ui_frame_offset);
        if (c_tcprtp_magic != APM_TCPRTP_HDR_MAGIC)
            goto redo;
        c_tcprtp_channel = *(p_payload + ui_frame_offset + 1);
        if (c_tcprtp_channel != 0x00)
            goto redo;
        p_us_rtp_len = (unsigned short *) (p_payload + ui_frame_offset + 2);
        us_rtp_len = ntohs(*p_us_rtp_len);
    }
    else if (stream_type == APM_RTP_STREAM_TYPE_TCPRTP)
    {
        stream_type = APM_RTP_STREAM_TYPE_TCPRTP_NONSTANDARD;
        ui_hdr_ext_len = APM_TCPRTP_NONSTANDARD_HDR_PKTLEN;
        ui_pkt_offset += ui_hdr_ext_len;
        p_us_rtp_len = (unsigned short *) (p_payload + ui_frame_offset);
        us_rtp_len = ntohs(*p_us_rtp_len);
    }
    else
        goto unknown;

    if (ui_payload_len - ui_hdr_ext_len < APM_RTP_HDR_LEN)
        goto unknown;

    if (ui_pkt_offset > ui_payload_len)
        goto redo;

    uc_tmp = (unsigned char) *(p_payload + ui_pkt_offset);
    i_version = APM_RTP_VERSION(uc_tmp);
    if (i_version != 2)
        goto redo;
    ui_pkt_offset += 1;

    i_padding = APM_RTP_PADDING(uc_tmp);
    i_extension = APM_RTP_EXTENSION(uc_tmp);
    i_cc_count = APM_RTP_CSRC_COUNT(uc_tmp);

    uc_tmp = (unsigned char) *(p_payload + ui_pkt_offset);
    i_marker = APM_RTP_MARKER(uc_tmp);
    i_payload_type = APM_RTP_PAYLOAD_TYPE(uc_tmp);
    ui_pkt_offset += 1;

    // sequence number
    p_us_seq = (unsigned short *) (p_payload + ui_pkt_offset);
    us_seq = ntohs(*p_us_seq);
    ui_pkt_offset += 2;

    // timestamp
    p_ui_timestamp = (unsigned int *) (p_payload + ui_pkt_offset);
    ui_timestamp = ntohl(*p_ui_timestamp);
    ui_pkt_offset += 4;

    // SSRC
    p_ui_ssrc = (unsigned int *) (p_payload + ui_pkt_offset);
    ui_ssrc = ntohl(*p_ui_ssrc);
    if (ui_ssrc == 0)
        goto redo;
    ui_pkt_offset += 4;

    // CSRC
    for (i_loop = 0; i_loop < i_cc_count; i_loop++)
        ui_pkt_offset += 4;

    if (ui_pkt_offset > ui_payload_len - ui_hdr_ext_len)
        goto redo;

    RTP_PRINT_ALERT("i_version:[%d]\n", i_version);
    RTP_PRINT_ALERT("i_padding:[%d]\n", i_padding);
    RTP_PRINT_ALERT("i_extension:[%d]\n", i_extension);
    RTP_PRINT_ALERT("i_cc_count:[%d]\n", i_cc_count);
    RTP_PRINT_ALERT("i_marker:[%d]\n", i_marker);
    RTP_PRINT_ALERT("i_payload_type:[%d]\n", i_payload_type);
    RTP_PRINT_ALERT("us_seq:[%d]\n", us_seq);
    RTP_PRINT_ALERT("ui_timestamp:[%u]\n", ui_timestamp);
    RTP_PRINT_ALERT("ui_ssrc:[%u]\n", ui_ssrc);

    if (p_st_detect_sess->c_detect_succ_count > 0)
    {
        if (p_st_detect_sess->us_pre_payload_type != i_payload_type ||
                p_st_detect_sess->us_pre_seq + 1 != us_seq ||
                p_st_detect_sess->ui_pre_ssrc != ui_ssrc ||
                p_st_detect_sess->ui_dst_ip != ui_dst_ip ||
                p_st_detect_sess->us_dst_port != us_dst_port)
            goto unknown;

        if (p_st_detect_sess->c_pre_is_marker == FR_NO)
        {
            if (p_st_detect_sess->ui_pre_timestamp != ui_timestamp)
                goto unknown;
        }
        else
        {
            if (p_st_detect_sess->ui_pre_timestamp > ui_timestamp)
                goto unknown;
        }
    }

    p_st_detect_sess->stream_type = stream_type;
    p_st_detect_sess->us_pre_payload_type = (unsigned short) i_payload_type;
    p_st_detect_sess->us_pre_seq = us_seq;
    p_st_detect_sess->ui_pre_timestamp = ui_timestamp;
    p_st_detect_sess->ui_pre_ssrc = ui_ssrc;
    p_st_detect_sess->ui_dst_ip = ui_dst_ip;
    p_st_detect_sess->us_dst_port = us_dst_port;
    p_st_detect_sess->c_pre_is_marker = (char) i_marker;
    p_st_detect_sess->c_detect_succ_count++;

    RTP_PRINT_NOTICE("detect_succ_count:[%u]\n", p_st_detect_sess->c_detect_succ_count);
    RTP_PRINT_NOTICE("current stream type:[%s]\n", APM_RTP_STREAMTYPE2STR(p_st_detect_sess->stream_type));
    RTP_PRINT_NOTICE("current payload type:[%u]\n", p_st_detect_sess->us_pre_payload_type);
    RTP_PRINT_NOTICE("current seq:[%u]\n", p_st_detect_sess->us_pre_seq);
    RTP_PRINT_NOTICE("current timestamp:[%u]\n", p_st_detect_sess->ui_pre_timestamp);
    RTP_PRINT_NOTICE("current ssrc:[%u]\n", p_st_detect_sess->ui_pre_ssrc);
    RTP_PRINT_NOTICE("current dst ip:[%u]\n", p_st_detect_sess->ui_dst_ip);
    RTP_PRINT_NOTICE("current dst port:[%u]\n", p_st_detect_sess->us_dst_port);
    RTP_PRINT_NOTICE("current is marker:[%s]\n", p_st_detect_sess->c_pre_is_marker != 0 ? "YES" : "NO");

    if (stream_type == APM_RTP_STREAM_TYPE_TCPRTP)
    {
        ui_frame_offset += APM_TCPRTP_HDR_PKTLEN + us_rtp_len;
        if (ui_payload_len < ui_frame_offset)
        {
            p_st_detect_sess->us_skip_len = (unsigned short) (ui_frame_offset - ui_payload_len);
        }
        else if (ui_payload_len > ui_frame_offset)
        {
            ui_pkt_offset = ui_frame_offset;
            p_st_detect_sess->us_skip_len = 0;
            goto redo;
        }
        else
            p_st_detect_sess->us_skip_len = 0;
    }
    else if (stream_type == APM_RTP_STREAM_TYPE_TCPRTP_NONSTANDARD)
    {
        ui_frame_offset += APM_TCPRTP_NONSTANDARD_HDR_PKTLEN + us_rtp_len;
        if (ui_payload_len < ui_frame_offset)
        {
            p_st_detect_sess->us_skip_len = (unsigned short) (ui_frame_offset - ui_payload_len);
        }
        else if (ui_payload_len > ui_frame_offset)
        {
            ui_pkt_offset = ui_frame_offset;
            p_st_detect_sess->us_skip_len = 0;
            goto redo;
        }
        else
            p_st_detect_sess->us_skip_len = 0;
    }
    RTP_PRINT_NOTICE("current skip len:[%u]\n", p_st_detect_sess->us_skip_len);

    if (p_st_detect_sess->c_detect_succ_count >= APM_RTP_DETECT_MAX)
        return APM_L7_IS_YES;
    else
        return APM_L7_IS_UNKNOWN;

unknown:
    rtp_init_detect_sess(&p_st_detect_sess);
    return APM_L7_IS_UNKNOWN;
}

int rtp_header_unpack(L7session_rtp *p_st_sess
                    , char *p_payload
                    , unsigned int ui_payload_len
                    , char *p_c_marker
                    , unsigned short *p_us_seq
                    , unsigned int *p_ui_timestamp
                    , unsigned int *p_ui_frame_len
                    , unsigned int *p_ui_skip_len)
{
    unsigned char uc_tmp;
    int i_version;
    int i_payload_type;
    int i_cc_count;
    int i_loop;
    unsigned short *p_us_seq_tmp;
    unsigned int *p_ui_timestamp_tmp;
    unsigned int *p_ui_ssrc;
    unsigned int ui_ssrc;
    unsigned int ui_hdr_ext_len = 0;
    unsigned short *p_us_rtp_len;
    unsigned short us_rtp_len = 0;
    unsigned int ui_pkt_offset = 0;
    char c_tcprtp_magic;
    char c_tcprtp_channel;

    RTP_PRINT_INFO("ui_payload_len:[%u]\n", ui_payload_len);

    if (p_st_sess->us_skip_len > 0)
    {
        if (p_st_sess->us_skip_len <= ui_payload_len) // 一帧结束
        {
            *p_c_marker = p_st_sess->c_pre_is_marker;
            *p_us_seq = p_st_sess->us_pre_seq;
            *p_ui_timestamp = p_st_sess->ui_pre_timestamp;
            *p_ui_frame_len = p_st_sess->ui_frame_len;
            *p_ui_skip_len = p_st_sess->us_skip_len;
            p_st_sess->us_skip_len = 0;
            p_st_sess->c_pre_is_marker = 0;
            p_st_sess->us_pre_seq = 0;
            p_st_sess->ui_pre_timestamp = 0;
            p_st_sess->ui_frame_len = 0;
            return FR_SUCC;
        }
        else // 需要下一个包
        {
            p_st_sess->us_skip_len = (unsigned short) (p_st_sess->us_skip_len - ui_payload_len);
            RTP_PRINT_CRIT("NEED NEXT PACKET, LENGTH:[%u]\n", p_st_sess->us_skip_len);
            goto need_next;
        }
    }

    if (p_st_sess->stream_type == APM_RTP_STREAM_TYPE_TCPRTP)
    {
        ui_hdr_ext_len = APM_TCPRTP_HDR_PKTLEN;
        ui_pkt_offset = ui_hdr_ext_len;
        c_tcprtp_magic = *p_payload;
        if (c_tcprtp_magic != APM_TCPRTP_HDR_MAGIC)
            goto error;
        c_tcprtp_channel = (unsigned char) *(p_payload + 1);
        RTP_PRINT_ALERT("TCP/RTP Channel:[0x%02x]\n", c_tcprtp_channel);
        if (c_tcprtp_channel != 0x00)
            goto need_next;
        p_us_rtp_len = (unsigned short *) (p_payload + 2);
        us_rtp_len = ntohs(*p_us_rtp_len);
    }
    else if (p_st_sess->stream_type == APM_RTP_STREAM_TYPE_TCPRTP_NONSTANDARD)
    {
        ui_hdr_ext_len = APM_TCPRTP_NONSTANDARD_HDR_PKTLEN;
        ui_pkt_offset = ui_hdr_ext_len;
        p_us_rtp_len = (unsigned short *) p_payload;
        us_rtp_len = ntohs(*p_us_rtp_len);
    }
    else
        us_rtp_len = (unsigned short) ui_payload_len;

    if (ui_payload_len - ui_hdr_ext_len < APM_RTP_HDR_LEN)
        goto error;

    uc_tmp = (unsigned char) *(p_payload + ui_pkt_offset);
    i_version = APM_RTP_VERSION(uc_tmp);
    if (i_version != 2)
        goto error;
    ui_pkt_offset += 1;

    i_cc_count = APM_RTP_CSRC_COUNT(uc_tmp);

    uc_tmp = (unsigned char) *(p_payload + ui_pkt_offset);
    *p_c_marker = (char) APM_RTP_MARKER(uc_tmp);
    i_payload_type = APM_RTP_PAYLOAD_TYPE(uc_tmp);
    ui_pkt_offset += 1;

    // sequence number
    p_us_seq_tmp = (unsigned short *) (p_payload + ui_pkt_offset);
    *p_us_seq = ntohs(*p_us_seq_tmp);
    ui_pkt_offset += 2;

    // timestamp
    p_ui_timestamp_tmp = (unsigned int *) (p_payload + ui_pkt_offset);
    *p_ui_timestamp = ntohl(*p_ui_timestamp_tmp);
    ui_pkt_offset += 4;

    // SSRC
    p_ui_ssrc = (unsigned int *) (p_payload + ui_pkt_offset);
    ui_ssrc = ntohl(*p_ui_ssrc);
    if (ui_ssrc == 0)
        goto error;
    ui_pkt_offset += 4;

    // CSRC
    for (i_loop = 0; i_loop < i_cc_count; i_loop++)
        ui_pkt_offset += 4;

    if (ui_pkt_offset > ui_payload_len - ui_hdr_ext_len)
        goto error;

    RTP_PRINT_NOTICE("is marker:[%s]\n", *p_c_marker != 0 ? "YES" : "NO");
    RTP_PRINT_NOTICE("seq:[%u]\n", *p_us_seq);
    RTP_PRINT_NOTICE("timestamp:[%u]\n", *p_ui_timestamp);

    if (us_rtp_len + ui_hdr_ext_len > ui_payload_len)
    {
        p_st_sess->us_skip_len = (unsigned short) (us_rtp_len + ui_hdr_ext_len - ui_payload_len);
        RTP_PRINT_CRIT("NEED NEXT PACKET, LENGTH:[%u]\n", p_st_sess->us_skip_len);
        p_st_sess->us_pre_seq = *p_us_seq;
        p_st_sess->ui_pre_timestamp = *p_ui_timestamp;
        p_st_sess->c_pre_is_marker = *p_c_marker;
        p_st_sess->ui_frame_len = us_rtp_len + ui_hdr_ext_len;
        goto need_next;
    }

    *p_ui_frame_len = us_rtp_len + ui_hdr_ext_len;
    *p_ui_skip_len = us_rtp_len + ui_hdr_ext_len;

    return 0;
need_next:
    return 1;
error:
    return 2;
}
